﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
using System.Data;
namespace Littlebeer.Deduplicate
{
    /// <summary>
    /// 去重工具类
    /// </summary>
    public class DeduplicateManager
    {
        /// <summary>
        /// 要查、去重的目标数据
        /// </summary>
        private DataTable _MainData;

        /// <summary>
        /// 目标数据表的主键（如果表格中没有，请先添加流水号，并设置为主键）
        /// </summary>
        string _PrimaryKey;

        /// <summary>
        /// 查重规则
        /// </summary>
        private List<Deduplicate.Models.DeduplicateRule> _DeduplicateRules;

        /// <summary>
        /// 每组最大成员数，如果超出限制，整个组则不视为重复，可能是规则不太严谨
        /// </summary>
        private int _GroupMaxCount = 10;

        public int GroupMaxCount
        {
            get { return _GroupMaxCount; }
            set { _GroupMaxCount = value; }
        }

        public DeduplicateManager()
        { }
        /// <summary>
        /// 初始化去重工具类
        /// </summary>
        /// <param name="MainData">要查、去重的目标数据</param>
        /// <param name="PrimaryKey">目标数据表的主键（如果表格中没有，请先添加流水号，并设置为主键）</param>
        /// <param name="DeduplicateRules">查重规则</param>
        public DeduplicateManager(DataTable MainData, string PrimaryKey, List<Deduplicate.Models.DeduplicateRule> DeduplicateRules)
        {
            _MainData = MainData;
            _PrimaryKey = PrimaryKey;
            _DeduplicateRules = DeduplicateRules;
        }
        public DeduplicateManager(int MaxCount, DataTable MainData, string PrimaryKey, List<Deduplicate.Models.DeduplicateRule> DeduplicateRules)
        {
            _GroupMaxCount = MaxCount;
            _MainData = MainData;
            _PrimaryKey = PrimaryKey;
            _DeduplicateRules = DeduplicateRules;
        }
        /// <summary>
        /// 执行查重，并取得查重结果
        /// </summary>
        /// <returns></returns>
        public List<Deduplicate.Models.DeduplicateResult> GetResult()
        {
            List<Deduplicate.Models.DeduplicateResult> result = new List<Models.DeduplicateResult>();

            //遍历所有规(查重规则应该不分先后顺序)
            foreach (Deduplicate.Models.DeduplicateRule rule in _DeduplicateRules)
            {
                //判断某一个规则中，不包含需要整体处理（比如相似度）的字段的个数。
                switch (rule.DeduplicateRuleFields.Where(d => !d.DeduplicateFunctions.Exists(s => s.DeduplicateFunctionType == Deduplicate.Models.DeduplicateFunctionType.Multiple)).Count())
                {
                    case (1):
                        {
                            ExeOneRule(result, rule);
                            break;
                        }
                    case (2):
                        {
                            ExeTwoRule(result, rule);
                            break;
                        }
                    case (3):
                        {
                            ExeThreeRule(result, rule);
                            break;
                        }
                    default:
                        {
                            throw (new Exception(string.Format("当前规则使用的字段数量{0}（支持最多3个）超出限制，无法处理。", rule.DeduplicateRuleFields.Count)));

                        }
                }
            }
            return ClearDeduplicateResult(result);
        }

        /// <summary>
        /// 单条件查询(目前没有收到可以动态生成N个条件的方法，暂时这样写死)
        /// </summary>
        /// <param name="result"></param>
        /// <param name="rule"></param>
        private void ExeOneRule(List<Deduplicate.Models.DeduplicateResult> result, Deduplicate.Models.DeduplicateRule rule)
        {
            //当规则类型为自动时，要求规则字段值不为空
            if (rule.RuleType == Deduplicate.Models.DeduplicateRuleType.Auto)
            {
                var query = from a in _MainData.AsEnumerable().AsParallel()//并行处理技术，有提升，但不明显，可能使用方式还需要改进
                            where a.Field<string>(rule.DeduplicateRuleFields[0].FieldName) != null && a.Field<string>(rule.DeduplicateRuleFields[0].FieldName).ToString().Trim() != ""//排除值为空的情况
                            group a by Littlebeer.Deduplicate.Functions.DeduplicateFunctionManager.DoFunctions(a.Field<string>(rule.DeduplicateRuleFields[0].FieldName), rule.DeduplicateRuleFields[0].DeduplicateFunctions) into g//先根对只有单行处理规则的字段进行加工，并分组
                            select new { DeduplicateReason = g.Key.ToString(), DeduplicateType = rule.RuleType, IDs = Deduplicate.Functions.DeduplicateFunctionManager.DoMutiFunctions(_PrimaryKey, g, rule) };//再对包含有多行处理函数的字段进行处理，并对分组结果进行排除。如前边前边处理结果一组中因公司名称一样的有A,B,C，但根据地址进行多行函数处理时，发现C与其它的相似度低于要求，因此，C要从结果集中排除。

                query.ForAll(d =>
                {
                    //将分组的结果添加到查重结果集中（望着排除掉空的情况）
                    if (d.IDs.Split(',').Where(s => s != "").Count() > 1 && d.IDs.Split(',').Where(s => s != "").Count() < _GroupMaxCount)
                    {

                        result.Add(new Deduplicate.Models.DeduplicateResult() { DeduplicateType = d.DeduplicateType, DeduplicateReason = d.DeduplicateReason, IDs = d.IDs });
                    }
                });
            }
            //当规则类型为人工确认时，不要求规则字段值不为空
            else
            {
                var query = from a in _MainData.AsEnumerable().AsParallel()//并行处理技术，有提升，但不明显，可能使用方式还需要改进
                            group a by Littlebeer.Deduplicate.Functions.DeduplicateFunctionManager.DoFunctions(a.Field<string>(rule.DeduplicateRuleFields[0].FieldName), rule.DeduplicateRuleFields[0].DeduplicateFunctions) into g//分组的依据是字段经过一系列加工后的值
                            select new { DeduplicateReason = g.Key.ToString(), DeduplicateType = rule.RuleType, IDs = Deduplicate.Functions.DeduplicateFunctionManager.DoMutiFunctions(_PrimaryKey, g, rule) };//再对包含有多行处理函数的字段进行处理，并对分组结果进行排除。如前边前边处理结果一组中因公司名称一样的有A,B,C，但根据地址进行多行函数处理时，发现C与其它的相似度低于要求，因此，C要从结果集中排除。

                query.ForAll(d =>
                {
                    if (d.IDs.Split(',').Where(s => s != "").Count() > 1 && d.IDs.Split(',').Where(s => s != "").Count() < _GroupMaxCount)
                    {
                        result.Add(new Deduplicate.Models.DeduplicateResult() { DeduplicateType = d.DeduplicateType, DeduplicateReason = d.DeduplicateReason, IDs = d.IDs });
                    }
                });
            }
        }

        /// <summary>
        /// 双条件(目前没有收到可以动态生成N个条件的方法，暂时这样写死)
        /// </summary>
        /// <param name="result"></param>
        /// <param name="rule"></param>
        private void ExeTwoRule(List<Deduplicate.Models.DeduplicateResult> result, Deduplicate.Models.DeduplicateRule rule)
        {
            //当规则类型为自动时，要求规则字段值不为空
            if (rule.RuleType == Deduplicate.Models.DeduplicateRuleType.Auto)
            {
                var query = from a in _MainData.AsEnumerable().AsParallel()//并行处理技术，有提升，但不明显，可能使用方式还需要改进
                            where a.Field<string>(rule.DeduplicateRuleFields[0].FieldName) != null && a.Field<string>(rule.DeduplicateRuleFields[0].FieldName).ToString().Trim() != "" && a.Field<string>(rule.DeduplicateRuleFields[1].FieldName) != null && a.Field<string>(rule.DeduplicateRuleFields[1].FieldName).ToString().Trim() != ""//排除值为空的情况
                            group a by new
                            {
                                t1 = Littlebeer.Deduplicate.Functions.DeduplicateFunctionManager.DoFunctions(a.Field<string>(rule.DeduplicateRuleFields[0].FieldName), rule.DeduplicateRuleFields[0].DeduplicateFunctions),//分组的依据，对第一个字段进行加工
                                t2 = Littlebeer.Deduplicate.Functions.DeduplicateFunctionManager.DoFunctions(a.Field<string>(rule.DeduplicateRuleFields[1].FieldName), rule.DeduplicateRuleFields[1].DeduplicateFunctions)//分组的依据，对第二个字段进行加工
                            } into g
                            select new { DeduplicateReason = g.Key.t1.ToString() + "+" + g.Key.t2.ToString(), DeduplicateType = rule.RuleType, IDs = Deduplicate.Functions.DeduplicateFunctionManager.DoMutiFunctions(_PrimaryKey, g, rule) };//再对包含有多行处理函数的字段进行处理，并对分组结果进行排除。如前边前边处理结果一组中因公司名称一样的有A,B,C，但根据地址进行多行函数处理时，发现C与其它的相似度低于要求，因此，C要从结果集中排除。

                query.ForAll(d =>
                {
                    if (d.IDs.Split(',').Where(s => s != "").Count() > 1 && d.IDs.Split(',').Where(s => s != "").Count() < _GroupMaxCount)
                    {
                        result.Add(new Deduplicate.Models.DeduplicateResult() { DeduplicateType = d.DeduplicateType, DeduplicateReason = d.DeduplicateReason, IDs = d.IDs });
                    }
                });
            }
            //当规则类型为人工确认时，不要求规则字段值不为空
            else
            {

                var query = from a in _MainData.AsEnumerable().AsParallel()//并行处理技术，有提升，但不明显，可能使用方式还需要改进                            
                            group a by new
                            {
                                t1 = Littlebeer.Deduplicate.Functions.DeduplicateFunctionManager.DoFunctions(a.Field<string>(rule.DeduplicateRuleFields[0].FieldName), rule.DeduplicateRuleFields[0].DeduplicateFunctions),
                                t2 = Littlebeer.Deduplicate.Functions.DeduplicateFunctionManager.DoFunctions(a.Field<string>(rule.DeduplicateRuleFields[1].FieldName), rule.DeduplicateRuleFields[1].DeduplicateFunctions)
                            } into g
                            select new { DeduplicateReason = g.Key.t1.ToString() + "+" + g.Key.t2.ToString(), DeduplicateType = rule.RuleType, IDs = Deduplicate.Functions.DeduplicateFunctionManager.DoMutiFunctions(_PrimaryKey, g, rule) };

                query.ForAll(d =>
                {
                    if (d.IDs.Split(',').Where(s => s != "").Count() > 1 && d.IDs.Split(',').Where(s => s != "").Count() < _GroupMaxCount)
                    {
                        result.Add(new Deduplicate.Models.DeduplicateResult() { DeduplicateType = d.DeduplicateType, DeduplicateReason = d.DeduplicateReason, IDs = d.IDs });
                    }
                });
            }
        }

        /// <summary>
        /// 三条件(目前没有收到可以动态生成N个条件的方法，暂时这样写死)
        /// </summary>
        /// <param name="result"></param>
        /// <param name="rule"></param>
        private void ExeThreeRule(List<Deduplicate.Models.DeduplicateResult> result, Deduplicate.Models.DeduplicateRule rule)
        {
            //当规则类型为自动时，要求规则字段值不为空
            if (rule.RuleType == Deduplicate.Models.DeduplicateRuleType.Auto)
            {

                var query = from a in _MainData.AsEnumerable().AsParallel()//并行处理技术，有提升，但不明显，可能使用方式还需要改进
                            where a.Field<string>(rule.DeduplicateRuleFields[0].FieldName) != null && a.Field<string>(rule.DeduplicateRuleFields[0].FieldName).ToString().Trim() != "" && a.Field<string>(rule.DeduplicateRuleFields[1].FieldName) != null && a.Field<string>(rule.DeduplicateRuleFields[1].FieldName).ToString().Trim() != "" && a.Field<string>(rule.DeduplicateRuleFields[2].FieldName) != null && a.Field<string>(rule.DeduplicateRuleFields[2].FieldName).ToString().Trim() != ""//排除值为空的情况
                            group a by new
                            {
                                t1 = Littlebeer.Deduplicate.Functions.DeduplicateFunctionManager.DoFunctions(a.Field<string>(rule.DeduplicateRuleFields[0].FieldName), rule.DeduplicateRuleFields[0].DeduplicateFunctions),//分组的依据，对第一个字段进行加工
                                t2 = Littlebeer.Deduplicate.Functions.DeduplicateFunctionManager.DoFunctions(a.Field<string>(rule.DeduplicateRuleFields[1].FieldName), rule.DeduplicateRuleFields[1].DeduplicateFunctions),//分组的依据，对第二个字段进行加工
                                t3 = Littlebeer.Deduplicate.Functions.DeduplicateFunctionManager.DoFunctions(a.Field<string>(rule.DeduplicateRuleFields[2].FieldName), rule.DeduplicateRuleFields[2].DeduplicateFunctions)//分组的依据，对第三个字段进行加工
                            } into g
                            select new { DeduplicateReason = g.Key.t1.ToString() + "+" + g.Key.t2.ToString() + "+" + g.Key.t3.ToString(), DeduplicateType = rule.RuleType, IDs = Deduplicate.Functions.DeduplicateFunctionManager.DoMutiFunctions(_PrimaryKey, g, rule) };//再对包含有多行处理函数的字段进行处理，并对分组结果进行排除。如前边前边处理结果一组中因公司名称一样的有A,B,C，但根据地址进行多行函数处理时，发现C与其它的相似度低于要求，因此，C要从结果集中排除。

                query.ForAll(d =>
                {
                    if (d.IDs.Split(',').Where(s => s != "").Count() > 1 && d.IDs.Split(',').Where(s => s != "").Count() < _GroupMaxCount)
                    {
                        result.Add(new Deduplicate.Models.DeduplicateResult() { DeduplicateType = d.DeduplicateType, DeduplicateReason = d.DeduplicateReason, IDs = d.IDs });
                    }
                });
            }
            else
            {
                var query = from a in _MainData.AsEnumerable().AsParallel()//并行处理技术，有提升，但不明显，可能使用方式还需要改进
                            group a by new
                            {
                                t1 = Littlebeer.Deduplicate.Functions.DeduplicateFunctionManager.DoFunctions(a.Field<string>(rule.DeduplicateRuleFields[0].FieldName), rule.DeduplicateRuleFields[0].DeduplicateFunctions),
                                t2 = Littlebeer.Deduplicate.Functions.DeduplicateFunctionManager.DoFunctions(a.Field<string>(rule.DeduplicateRuleFields[1].FieldName), rule.DeduplicateRuleFields[1].DeduplicateFunctions),
                                t3 = Littlebeer.Deduplicate.Functions.DeduplicateFunctionManager.DoFunctions(a.Field<string>(rule.DeduplicateRuleFields[2].FieldName), rule.DeduplicateRuleFields[2].DeduplicateFunctions)
                            } into g
                            select new { DeduplicateReason = g.Key.t1.ToString() + "+" + g.Key.t2.ToString() + "+" + g.Key.t3.ToString(), DeduplicateType = rule.RuleType, IDs = Deduplicate.Functions.DeduplicateFunctionManager.DoMutiFunctions(_PrimaryKey, g, rule) };

                query.ForAll(d =>
                {
                    if (d.IDs.Split(',').Where(s => s != "").Count() > 1 && d.IDs.Split(',').Where(s => s != "").Count() < _GroupMaxCount)
                    {
                        result.Add(new Deduplicate.Models.DeduplicateResult() { DeduplicateType = d.DeduplicateType, DeduplicateReason = d.DeduplicateReason, IDs = d.IDs });
                    }
                });
            }
        }

        /// <summary>
        /// 过滤掉分组中的子集，减少人工确认的量：1.人工组是自动子集（过滤）；2.人工组是人工组子集（过滤）；3.自动组是自动组子集（过滤）
        /// </summary>
        /// <param name="DeduplicateResult"></param>
        /// <returns></returns>
        public List<Deduplicate.Models.DeduplicateResult> ClearDeduplicateResult(List<Deduplicate.Models.DeduplicateResult> DeduplicateResult)
        {
            List<Deduplicate.Models.DeduplicateResult> result = DeduplicateResult;

            var query_auto = from a in DeduplicateResult.Where(d =>d!=null&& d.DeduplicateType == Deduplicate.Models.DeduplicateRuleType.Auto)
                             select a;

            var query_manual = from b in DeduplicateResult.Where(d =>d!=null&& d.DeduplicateType == Deduplicate.Models.DeduplicateRuleType.Manual)
                               select b;

            if (query_auto.Count() > 0 && query_manual.Count() > 0)
            {
                //1.人工组是自动子集（过滤）
                var query1 = from a in query_auto
                             join b in query_manual on 1 equals 1
                             where a.IDs.Length > b.IDs.Length
                             && CheckIfContain(a.IDs, b.IDs)
                             select b;
                if (query1.Count() > 0)
                {
                    result = result.Except(query1.ToList<Deduplicate.Models.DeduplicateResult>()).ToList<Deduplicate.Models.DeduplicateResult>();
                }
            }
            if (query_auto.Count() > 0)
            {
                //2.自动组是自动组子集（过滤）
                var query3 = from a in query_auto
                             join b in query_auto on 1 equals 1
                             where a.IDs.Length > b.IDs.Length
                            && CheckIfContain(a.IDs, b.IDs)
                             select b;
                if (query3.Count() > 0)
                {
                    result = result.Except(query3.ToList<Deduplicate.Models.DeduplicateResult>()).ToList<Deduplicate.Models.DeduplicateResult>();
                }
            }
            return result;
        }
        private Boolean CheckIfContain(string str1, string substr2)
        {
            Boolean result = false;
            string[] a = str1.Split(',');
            string[] b = substr2.Split(',');
            result = a.Intersect(b).Count() == b.Count();
            return result;
        }
    }
}
